/* Class Name: WorkflowEditController 
 * Author: Accenture
 * Date: 21-Oct-2011
 * Requirement/Project Name: iFulfill
 * Requirement Description: Controller for Workflow Edit page.
 **/

public with sharing class WorkflowController 
{
    public string wrkflwStgSeq{get;set;} 
    public id workflowId{get;set;}
    public boolean IsWFCreated{get;set;}
    public boolean IsSTGCreated{get;set;}
    public boolean IsAGCreated{get;set;}    
    public list<stageWrapper> Stages{get;set;}
    public list<stage_definition__c> selectedStages{get;set;}
    public list<wrapperOut> wrapOut{get;set;}   
    public List<AGWrapper> asgnGroups{get;set;} 
    public boolean renderSaveButton {get;set;}        
    Workflow_definition__c WF ; 
    private Map<ID,List<Activities_definition__c>> StepActMAP;  
    List<AGWrapper> selectedGroups;  
    Map<string,id> funcNameMap =  new Map<string,id>();
    list<Workflow_Stage__c> WFStageList = new list<Workflow_Stage__c>();
    list<WF_Assignment_Group__c> WFGroupList = new list<WF_Assignment_Group__c>();
    list<Workflow_Activities__c> WFActList = new list<Workflow_Activities__c>();   
      
    Map <String,List<Decimal>> sequenceStepMap;  
    Map<string,id> stageNameMap =  new Map<string,id>();   
    Map<id,id> stageMap = new Map<id,id>(); 
    transient Savepoint sp;
    
    /*
     *  Constructor 
     *
     **/      
    public WorkflowController(ApexPages.StandardController controller)
    {
        WF = (Workflow_definition__c)controller.getRecord();
        renderSaveButton  = false;  
        asgnGroups = new  List<AGWrapper>(); 
        wrapout = new list<wrapperOut>();                                              
    }
        
   // Wrapper class for Assignment group    
    public Class AGWrapper{
        public Assignment_Group_Name__c AGroup{get;set;}
        public boolean slctbox{get;set;}
        public string functionName{get;set;}               
        public AGWrapper(Assignment_Group_Name__c G, boolean chkbox, string func){
            AGroup=G;
            slctbox =chkbox;
            functionName = func;
        }
    }
    
    // Wrapper class for workflow stage         
  public   Class StageWrapper{
        public stage_definition__c SD{get;set;}
        public boolean selectbox{get;set;}        
        public stageWrapper(stage_definition__c S, boolean chkbox){
            SD =S;
            selectbox =chkbox;
        }
    }
 // Wrapper class for workflow Step
   public  Class StepWrapper{
        public step_definition__c StepDef{get;set;}
        public boolean selbox{get;set;} 
        public string defaultAG{get;set;} 
        public List<SelectOption> funcGroups{get;set;}        
        public stepWrapper(step_definition__c S, boolean chkbox, string defaultGrp,List<SelectOption> funcGrps ){
            StepDef =S;
            selbox =chkbox;
            defaultAG = defaultGrp;
            funcGroups = funcGrps ;
        }
        public stepWrapper(){}
    }
      // Wrapper class for Activity Def 
  public   Class ActWrapper{
        public Activities_definition__c AD{get;set;}
        public boolean slctbox{get;set;}        
        public ActWrapper(Activities_definition__c A, boolean chkbox){
            AD =A;
            slctbox =chkbox;
        }
        public ActWrapper(){}
    }
    
  public   Class WrapperOut{
        public List<ActWrapper> Act{get;set;}
        public stepWrapper step{get;set;}        
        public wrapperOut(){
            if(step==null){step= new stepWrapper();}
            if(Act==null){Act= new List<ActWrapper>();}           
        }
    }
    /*
     *  Save the WorkFlow
     *
     */
        
    public pagereference saveWF()
    {
        sp = Database.setSavepoint();  
        if(WF.id == null)
        {
            WF.Version__c = 1;
            Insert WF;
            IsWFCreated = true;
        }
        else Update WF; 
        workflowId = WF.id;       
        Stages = new list<stageWrapper>();
        List<Stage_definition__c> activeStages = [select name ,sequence__C from stage_definition__c where status__c = 'active' order by sequence__C];
        
        for(Stage_definition__c ST:activeStages)
        {
                Stages.add(new stageWrapper(ST, false));
        }  
        return null;
    }

   /*  
    *  Action associated with Add Function Button
    *
    **/ 
   public pagereference saveStages()
     {
        selectedStages = new list<stage_definition__C>();
        set<integer> uniqueSequence = new set<integer>();  
        list<AGWrapper> asgnGroups1 = new list<AGWrapper>(); 
        Integer count = 0;
        for(integer i=0 ; i< Stages.size(); i++)
        {
            // Check if user has selected the check box
            if(Stages[i].selectBox)
            {
                count++;
                Stages[i].SD.Sequence__c = Math.ceil(Math.abs(Integer.valueof(Stages[i].SD.sequence__c)));
                
                 // Check if user has enter any duplicate function sequence  else display error msg
                if(!uniqueSequence.contains(Integer.valueof(Stages[i].SD.sequence__c)) )
                {
                    uniqueSequence.add(Integer.valueof(Stages[i].SD.sequence__c));
                    selectedStages.add(Stages[i].SD);
                    System.debug('selectedStages --->'+selectedStages);
                    funcNameMap.put(Stages[i].SD.name, Stages[i].SD.id); 
                    list<Assignment_Group_Name__c> Groups = [select name, id from Assignment_Group_Name__c where function__c  INCLUDES (:Stages[i].SD.Name) and type__c = 'Fulfillment'];               
                    
                    for(Assignment_Group_Name__c Grp:Groups)
                    {
                        asgnGroups1.add(new AGWrapper(Grp, false,Stages[i].SD.Name));
                    }                      
                }
                else
                {
                    ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Duplicate Sequence On Function');
                    ApexPages.addMessage(msg);
                    selectedStages.clear();
                    return null;
                }
            }
        } 
        
        // Check if user has select any function if not display error msg
        if(count == 0)
        {
            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please select at least one Function.');
            ApexPages.addMessage(msg);
            selectedStages.clear();
            return null;
        }
  
        // Check if user has enter the valid sequence no.  
        if(validateStageSequenceOrder()){                    
             ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please Check Function Sequence Order');
             ApexPages.addMessage(msg);    
             selectedStages.clear();
             return null;         
        }
       // check if user has selected any stage
      if(selectedStages.size()>0){
            IsSTGCreated = true;  
            asgnGroups = asgnGroups1 ;                     
         }
        return null; 
     }
    
    /*
     *  Action associated with add group button
     *
     **/
    public pagereference saveGroup(){
        renderSaveButton = true;  
        selectedGroups = new List<AGWrapper>();     
       for(AGWrapper A: asgnGroups ){
           if(A.slctbox){
           selectedGroups.add(A);}
       }
       
       // Check id user has selected one assignment group per function
       if(validateAssignmentGroup() )
       {
            wrapout = new list<wrapperOut>();
            StepActMAP = new Map<ID,List<Activities_definition__c>>();
            List<Step_definition__c> StepList = [select id,name,step_definition__c.Stage__r.name,Step_Sequence__c,Target_Days__c,Dashboard_Function__c from step_definition__c where status__c='Active' and stage__c = :selectedStages order by stage__c];        
            for(Activities_definition__c ActList : [select id,name,Activity_Description__c, status__c,required__C,step__r.name,step__c,step__r.Stage__r.name,step__r.Target_Days__c,step__r.Dashboard_Function__c from Activities_definition__c where status__c = 'Active' and step__c in :StepList]){
                if(StepActMAP.containsKey(ActList.step__c)){
                    StepActMAP.get(ActList.step__c).add(ActList);//adds activities for this step to the activity list in the map    
                }else{
                    StepActMAP.put(ActList.step__c,new List<Activities_definition__c>{ActList});//adds new activity list for this step to the map    
                }                            
            }           
            for(Step_definition__c stdef :StepList){
                wrapperOut tmpwrapper = new wrapperOut();
                list<SelectOption> tskGrpOptions = new list<SelectOption>();
                for(AGWrapper Gp :selectedGroups){
                    if(stdef.Stage__r.name == Gp.functionName)
                     tskGrpOptions.add(new SelectOption(Gp.AGroup.id,Gp.AGroup.Name)); 
                }                            
                 tmpwrapper.Step=new stepWrapper(stdef, false,'',tskGrpOptions);
   //              tmpwrapper.Step=new stepWrapper(stdef, false,'--None--',tskGrpOptions);
                List<ActWrapper> twrap2=new List<ActWrapper>(); 
                if(StepActMAP.containsKey(stdef.id)){
                    for(Activities_definition__c ActDef : StepActMAP.get(stdef.id)){
                        twrap2.add(new ActWrapper(ActDef, False));
                    }
                }
                tmpwrapper.Act = twrap2 ;                
                wrapout.add(tmpwrapper);
            }         
       return null;
       }
       else
       {
             renderSaveButton  = false;
              ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please select at least one Assignment Group Per Function');
              ApexPages.addMessage(msg);  
               // Clear wrapout list in case user not selected assignment group  for  the function
              if (wrapout!=null)
                   wrapout.clear();                  
              return null;              
         }
         
     return null;              
         
    }    
   
     /*
      * Saving Functions, Groups, Tasks and Activities   
      *
      **/     
     public pagereference saveSteps()
     {
          //Insert Functions
          if(WFStageList.size()<1)  insertWFFunctions();                            
          list<Workflow_Stage__c> StageIds = [select id, stage__c, name from Workflow_Stage__c where Workflow_Code__c = :WF.id];
          for(Workflow_Stage__c s: StageIds){
              stageMap.put(s.stage__c,s.id);
              stageNameMap.put(s.name, s.id);
          }          
         //Insert Groups
         inserWFGroups();         
         //Insert Tasks
         if(insertWFTasks())  return null;                                        
         //Insert Activities
         insertWFActivities();
         Pagereference Page = new Pagereference('/apex/WorkflowdefinitionSummaryView?id='+WF.id); 
         iFulfillHandler.updateLastTaskAndFunction(WF.Id);
         return Page;
     }
     
    /*
     *  Validate the stage sequence Order
     *
     */
      private boolean validateStageSequenceOrder()
      {      
          Decimal maximumSequence =0.0;          
          for(stage_definition__c S:selectedStages)
          {
           if(S.Sequence__c >maximumSequence)
           {
               maximumSequence =S.Sequence__c;
           }            
          }          
          if(maximumSequence.intValue() !=  selectedStages.size()){
             return true;
          }  
          return false; 
      } 
    /*
     *  Validate the step sequence Order
     *
     **/
     private boolean validateStepSequenceOrder(){                      
           for(stage_definition__c S:selectedStages){
              List<Decimal> stepList = new List<Decimal>();
              if(sequenceStepMap.containsKey(S.id)){
               stepList = sequenceStepMap.get(S.id);
               if(stepList.size()>0){
                   stepList.sort();
                   if(stepList.size() !=stepList[stepList.size()-1].intValue()){
                       return true;
                   }
                }
               }     
            }            
           return false;
     }
    /*
     *  Insert the workflow Function
     *
     **/ 
     private void insertWFFunctions(){
                for(stage_definition__c S:selectedStages){        
                        Workflow_Stage__c WFStage = new Workflow_Stage__c();
                        WFStage.Workflow_Code__c = WF.id;
                        WFStage.Name = S.name;
                        WFStage.Stage__c = S.id;
                        WFStage.sequence__C = S.sequence__C;
                        WFStageList.add(WFStage); 
                }     
                    //Save only if not already saved
                    if(WFStageList.size()>0 && WFStageList[0].id == null){
                        try{insert WFStageList;}
                        Catch(DMLException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowCOntroller','SaveSteps: Insert Stages failed',e);} 
                }
     
     }
    /*
     *  Insert the workflow assignment group
     *
     **/
     Private void inserWFGroups(){
                for(AGWrapper grp :selectedGroups){
                    WF_Assignment_Group__c WFgrp = new WF_Assignment_Group__c();
                    WFgrp.Name = grp.AGroup.Name;
                    WFgrp.AG_Definition__c = grp.AGroup.id;
                    WFgrp .Workflow_Function__c = stageNameMap.get(grp.functionName);
                    WFGroupList.add(WFgrp); 
                 }
                 //Save only if not already saved
                 if (WFGroupList.size()>0 && WFGroupList[0].id == null){
                     try{insert WFGroupList;}
                     catch(dmlexception e){GeneralConstants.mailAnyExceptionToMe('WorkFLowCOntroller','SaveGroup: Insert Groups failed',e);}
                 }
     }
     /*
     *  Insert the workflow task     
     *
     **/ 
     Private boolean insertWFTasks(){
            list<Wokflow_Steps__c> WFStepList = new list<Wokflow_Steps__c>(); 
            sequenceStepMap= new Map <String,List<Decimal>>();          
            set<string> uniqueSeq = new set<string>();    
     
            for(integer i=0 ; i< wrapOut.size(); i++)
            {
       
                if(wrapOut[i].Step.selbox)
                {
                    if( wrapOut[i].Step.defaultAG == null || wrapOut[i].Step.defaultAG == '')
                    {
                            WFStepList.clear();
                            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please select at least one Assignment Group Per Function');
                            ApexPages.addMessage(msg);                    
                            return true;                    
                    }
                    else{
                        if(sequenceStepMap.containsKey(wrapOut[i].Step.StepDef.stage__c)){
                           sequenceStepMap.get(wrapOut[i].Step.StepDef.stage__c).add(wrapOut[i].Step.StepDef.step_sequence__C);
                        }else{
                           sequenceStepMap.put(wrapOut[i].Step.StepDef.stage__c,new List<Decimal>{wrapOut[i].Step.StepDef.step_sequence__C});
                        }   
                        if(!uniqueSeq.contains(wrapOut[i].Step.StepDef.stage__c + String.valueof(Integer.valueof(wrapOut[i].Step.StepDef.step_sequence__C)))){
                            uniqueSeq.add(wrapOut[i].Step.StepDef.stage__c + String.valueof(Integer.valueof(wrapOut[i].Step.StepDef.step_sequence__C)));
                            Wokflow_Steps__c WFStep = new Wokflow_Steps__c();
                            WFStep.Wokflow_Stage_Name__c = stageMap.get(wrapOut[i].Step.StepDef.stage__c);
                            WFStep.Name = wrapOut[i].Step.StepDef.name;
                            WFStep.Step__c = wrapOut[i].Step.StepDef.id;
                            WFStep.sequence__C = (wrapOut[i].Step.StepDef.step_sequence__C);
                            WFStep.Target_Days__c = wrapOut[i].Step.StepDef.Target_Days__C;
                            WFStep.Dashboard_Function__c = wrapOut[i].Step.StepDef.Dashboard_Function__C;
                            WFStep.Default_Group__c =  wrapOut[i].Step.defaultAG ;
                            WFStepList.add(WFStep);
                        }
                        else{
                            WFStepList.clear();
                            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Duplicate Sequence On Tasks');
                            ApexPages.addMessage(msg);                    
                            return true;
                        }
                    }
                }
               
            } 
             // Check if user has selected atleast one task per func
            if(validateTask())
            {
                 ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Select at least one task per Function');
                 ApexPages.addMessage(msg);    
                 return true;                      
            
            }
             // Check if user has entered valid sequence order
            if(validateStepSequenceOrder()){ 
             ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please Check Task Sequence Order');
             ApexPages.addMessage(msg);    
             return true;                      
            }
                  
            if(WFStepList.size()>0 && WFStepList[0].id == null){
                try{Insert WFStepList;}
                catch(dmlException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowController','SaveSteps: Insert Steps failed',e);}
            } 
            
            return false;
     
     }
    /*
     *  insert the Work Flow activites.
     *
     **/
     private void insertWFActivities(){
         map<id,id> stepMap = new Map<id,id>();
         list<Wokflow_Steps__c> StepIds = [select id, step__c from Wokflow_Steps__c where Wokflow_Stage_Name__r.Workflow_Code__c = :WF.id];
            for(Wokflow_Steps__c  s: StepIds ){
                stepMap.put(s.step__c,s.id);
            } 
            for(integer i=0 ; i< wrapOut.size(); i++){
              for(integer j=0 ; j< wrapOut[i].Act.size(); j++){
                // Check if user has selected the activities
                if(wrapOut[i].Act[j].slctbox){
                    Workflow_Activities__c WFAct = new Workflow_Activities__c();
                    WFAct.Step_Name__c = stepMap.get(wrapOut[i].Act[j].AD.step__c);
                    WFAct.Name = wrapOut[i].Act[j].AD.name;
                    WFAct.Activity__c = wrapOut[i].Act[j].AD.id;
                    WFAct.required__C = boolean.valueof(wrapOut[i].Act[j].AD.Required__C);
                    WFActList .add(WFAct);
                }}
            }       
            if(WFActList.size()>0 && WFActList[0].id == null){
                try{Insert WFActList;}
                catch(dmlException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowController','SaveSteps: Insert Steps failed',e);}
            }                       
     }
     
       
    /*
     *  Action associated with Cancel button.
     *
     **/
     public pagereference doCancel(){     
         Database.rollback(sp);
         pagereference retPage = new pagereference('/'+GeneralConstants.Workflow_definition_Prefix+'/o');
         return retPage ;
         
     }
       /*
      *  Check user has selected atleast one task per function
      *
      **/
     private boolean validateTask()
     {
         boolean status = false;
         Set<ID> selectedStageName = new Set<ID>();
         Set<ID> selectedStage1 = new Set<ID>();
         
         
         for(integer i=0 ; i< selectedStages.size(); i++)
         {
            
                selectedStageName.add(selectedStages[i].id);
           
         }
         for(integer i=0 ; i< wrapOut.size(); i++)
         {
       
                if(wrapOut[i].Step.selbox)
                {
                    selectedStage1.add(wrapOut[i].Step.StepDef.stage__c);
                }
          }
          
          if(selectedStageName.size() != selectedStage1.size())
          {
              status = true;
          }
          return status;
     }

  /*
    *  Check user has selected atleast one Assignment group per function
    *
    **/ 
      private boolean validateAssignmentGroup ()
      {
         boolean status = true ;
         Integer count = 0;
         Set<String> selectedStageName = new Set<String>();
         for(integer i=0 ; i< selectedStages.size(); i++)
         {
                selectedStageName.add(selectedStages[i].Name);           
         }
         for(String  strObj : selectedStageName)
         {
                 count = 0;
               
                 for(AGWrapper agwrapperObj: asgnGroups )
                 {
                  // Check if user has selected check box and for each function
                     if(agwrapperObj.slctbox && strObj ==agwrapperObj.functionName )
                     {
                         count++;
                     }
                 }
                 //  if user has not selected any assignment group for the func exit the loop
                 if(count == 0)
                 {
                   status  = false;
                   break;                         
                 }                
         }        
         return status;
      }
}